
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/



#include "errno.h"
#include "miniutil/usocket.h"

using namespace std;
using namespace miniutil;

MINI_DEFI_CLASS_COUNTER_VALUE(usocket, usocket_send_all_byte)
MINI_DEFI_CLASS_COUNTER_VALUE(usocket, usocket_recv_all_byte)

MINI_BEGIN_STATIC_COUNTER_GROUP(miniutil_usocket)
MINI_STATIC_COUNTER("miniutil::usocket::usocket_send_all_byte", usocket::usocket_send_all_byte)
MINI_STATIC_COUNTER("miniutil::usocket::usocket_recv_all_byte", usocket::usocket_recv_all_byte)
MINI_END_STATIC_COUNTER_GROUP

usocket::usocket()
{
	this->hasBound = this->hasConnected = this->hasClosed = false;

	this->sock = ::socket(PF_INET, SOCK_DGRAM, 0);
	if ( sock <= 0 )
		throw miniutil::socketException("can not open socket!");
}

usocket::usocket( const miniutil::soaddr & bindAddr )
{
	this->hasBound = this->hasConnected = this->hasClosed = false;

	this->sock = ::socket(PF_INET, SOCK_DGRAM, 0);
	if ( sock <= 0 )
		throw miniutil::socketException("can not open socket!");

	this->bind( bindAddr );
}

usocket::~usocket()
{
	this->close();
}

void usocket::close()
{
	if ( this->hasClosed )
		return;
	if ( this->sock > 0 )
	{
#ifdef WIN32
		closesocket(sock);
#else
		::close(sock);
#endif
	}
	sock = 0;
	this->hasClosed = true;
}


bool usocket::bind( int port, const miniutil::ip4addr & addr )
{
	if ( this->hasClosed || this->hasBound ) return false;

	return this->bind( soaddr(port, addr) );
}

bool usocket::bind(const  miniutil::soaddr & bindAddr )
{
	if ( this->hasClosed || this->hasBound ) return false;
	
	if ( ((miniutil::soaddr &)bindAddr).getPort() == 0 ) return false;

	this->bindAddress = bindAddr;
	if ( ::bind( sock, bindAddress.get_sockaddr(), sizeof(struct sockaddr_in)) != 0 )
		return false;

	this->hasBound = true;
	return true;
}

bool usocket::connect(const char * host, int port )
{
	if ( this->hasClosed || this->hasConnected ) return false;

	miniutil::soaddr saddr(port, ip4addr(host)); 

	return this->connect( saddr );
}

bool usocket::connect( const miniutil::soaddr & cAddr)
{
	if ( this->hasClosed || this->hasConnected ) return false;
 miniutil::soaddr nAddr(cAddr);
	if ( nAddr.getPort() == 0 ) return false;
	if ( nAddr.getIP() == ip4addr::INETANY() ) return false;

	this->connectAddress = (cAddr);
	if ( ::connect(sock, this->connectAddress.get_sockaddr(), sizeof(struct sockaddr_in)) != 0 )
		return false;

	this->hasConnected = true;
	return true;
}

int usocket::send( const char * data, int len )
{
	if ( this->hasClosed ) return -1;

	if ( ! this->hasConnected ) 
		return this->sendto( data, len , this->sendtoAddress );

	int slen = ::send(sock, data, len , 0 );		
	if ( slen < 0 )
	{
		int errcode = socket::getLastError();
#ifdef WIN32
		if ( errcode == WSAEWOULDBLOCK ) 
			return 0;
#else
		if( errcode == EAGAIN || errcode == ENOENT || errcode == EWOULDBLOCK )
			return 0;
#endif
	}
	usocket_send_all_byte += slen;
	return slen;
}

int usocket::sendto( const char * data, int len , miniutil::soaddr & sAddr )
{
	if ( this->hasClosed ) return -1;
	if ( sAddr.getPort() == 0 ) return -1;
	if ( sAddr.getIP() == ip4addr::INETANY() ) return -1;

	int slen = ::sendto(sock, data, len, 0, sAddr.get_sockaddr(), sizeof(struct sockaddr_in) );
	if ( slen < 0 )
	{
		int errcode = socket::getLastError();
#ifdef WIN32
		if ( errcode == WSAEWOULDBLOCK ) 
			return 0;
#else
		if( errcode == EAGAIN || errcode == ENOENT || errcode == EWOULDBLOCK )
			return 0;
#endif
	}
	usocket_send_all_byte += slen;
	return slen;
}

int usocket::recvfrom(char * data, int maxlen,  miniutil::soaddr & fromAddr )
{
	if ( this->hasClosed ) return -1;
	if ( data == NULL ) return -1;

	socklen_t salen = sizeof(struct sockaddr_in);
	struct sockaddr_in from;
	int slen = ::recvfrom( sock, data, maxlen,0, (struct sockaddr*)&from, &salen );
	if ( slen < 0 )
	{
		int errcode = socket::getLastError();
#ifdef WIN32
		if ( errcode == WSAEWOULDBLOCK ) 
			return 0;
#else
		if( errcode == EAGAIN || errcode == ENOENT || errcode == EWOULDBLOCK )
			return 0;
#endif
		return slen;
	}
	usocket_recv_all_byte += slen;
	soaddr nAddr( &from );
	fromAddr.setIP( nAddr.getIP() );
	fromAddr.setPort( nAddr.getPort() );

	return slen;
}

upacket * usocket::recv()
{
    char buf[2050];
    miniutil::soaddr peer;

    int len = this->recvfrom( buf, 2048, peer ) ;
    if ( len <= 0 )
        return NULL;

    return new upacket( buf, len , peer );
}

bool miniutil::usocket::setBlockMode( bool isblocked )
{
#if defined(WIN32)

	u_long blockmode = (isblocked ? 0 : 1);
	if (ioctlsocket(sock, FIONBIO, &blockmode)) 
		return false;
#else

	if (fcntl(sock, F_SETFL, (isblocked ? 0 : O_NONBLOCK)) == -1) 
		return false;

#endif
	return true;

}

bool miniutil::usocket::waitWriteable(int ms)
{
	fd_set writefs;
	timeval timeout;

	timeout.tv_sec = ms /1000;
	timeout.tv_usec = ( ms % 1000 ) * 1000;
	FD_ZERO( & writefs );
	FD_SET( this->sock, & writefs );

	int ret = select( (int)(this->sock) + 1, NULL, &writefs, NULL, &timeout );
	if ( ret < 0 )
		throw socketException( "select() error!" );
	else if ( ret == 0 )
		return false; // timeout
	else if ( ! FD_ISSET( this->sock, &writefs ) )
		return false; // immpossible!!!
	else
		return true;
}


bool miniutil::usocket::waitReadable(int ms)
{
	fd_set readfs;
	timeval timeout;

	timeout.tv_sec = ms /1000;
	timeout.tv_usec = ( ms % 1000 ) * 1000;
	FD_ZERO( & readfs );
	FD_SET( this->sock, & readfs );

	int ret = select( (int)(this->sock) + 1,  &readfs, NULL, NULL, &timeout );
	if ( ret < 0 )
		throw socketException( "select() error!" );
	else if ( ret == 0 )
		return false; // timeout
	else if ( ! FD_ISSET( this->sock, &readfs ) )
		return false; // immpossible!!!
	else
		return true;
}
